关于Python Decorator你应该知道的一切

转载并整理格式自关于Python Decorator你应该知道的一切

关键词:Python、装饰器、装饰器的深入讨论、Python decorator

前言

最近学习Python,在看一个框架源码过程中对装饰器很困惑,Google了一圈,在stack overflow的一个问题讨论下面找到了这个总结,这里几乎有关于Python全部的内容。觉得很好,遂翻译过来。翻译基本都是意译,能看英文的还是尽量看上面链接过去的原版吧!

Pyton装饰器基础

在Python中,函数也是对象

为了理解装饰器,你必须首先理解,在Python中函数也是对象。
理解这个知识点很重要。让我们使用一个简单的例子来说明一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def shout(word="yes"):
return word.capitalize()+"!"

print shout()
# 输出为: 'Yes!'


# 函数作为一个对象,你可以像其他对象一样,把它赋值给其他的变量
scream = shout

# 注意我们没有使用圆括号:我们不是调用这个函数,我们把"shout"这个函数赋值给变量"scream"
# 那意味着你之后可以使用"scream"来调用"shout"这个函数
print scream()
# 输出为: 'Yes!'

# 不仅如此,那意味着你可以移除'shout'这个老的名称,但这个函数仍然可以通过'scream'访问
del shout
try:
print shout()
except NameError, e:
print e
# 输出为: "name 'shout' is not defined"

print scream()
# 输出为: 'Yes!'

好了,在心里记住这个知识点。我们之后很快要用到它。
Python中函数还有另一个有趣的特性,那就是它可以在其他函数里面定义!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def talk():
# 你可以在"talk"函数中定义一个函数...
def whisper(word="yes"):
return word.lower()+"..."

# ...并且你可以马上使用这个函数
print whisper()

# 你每次调用"talk"这个函数的时候,它会定义一个"whisper"函数,之后这个"whisper"将在"talk"里面被调用
talk()
# 输出为:"yes..."

# 但是在"talk"这个函数的作用域之外,"whisper"这个函数是不存在的
try:
print whisper()
except NameError, e:
print e
# 输出为: "name 'whisper' is not defined"*

函数的引用

Okay,就这些东西吗?有趣的部分该上场了…
你已经看见,函数是对象。因此,函数:

  • 可以赋值给其他变量
  • 可以在其它函数里面定义

那意味着一个函数可以被另一个函数return。我们来看个例子! ☺

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def getTalk(kind="shout"):

# 我们定义了一些函数
def shout(word="yes"):
return word.capitalize()+"!"

def whisper(word="yes") :
return word.lower()+"...";

# 然后我们返回他们中的一个
if kind == "shout":
# 我们没有用"()", 我们不是要调用这个函数
# 我们返回了这个函数对象
return shout
else:
return whisper

# 我们怎么使用它呢?
# 获取函数,并将它赋值给一个变量
talk = getTalk()

# 你可以看到在这里"talk"是一个函数对象:
print talk
# 输出为: <function shout at 0xb7ea817c>;

# 这个就是被函数返回的对象
print talk()
# 输出为: Yes!

# 你甚至可以直接使用它:
print getTalk("whisper")()
# 输出为: yes...

等等…这里有我们没有注意到的地方!
既然你可以return一个函数,你就可以把一个函数当参数传递:

1
2
3
4
5
6
7
8
def doSomethingBefore(func): 
print "I do something before then I call the function you gave me"
print func()

doSomethingBefore(scream)
# 输出为:
#I do something before then I call the function you gave me
#Yes!

好了,你已经具备了理解装饰器的所有知识点。你知道,装饰器就是 “封装”, 这意味着它可以让你在被它装饰的函数前面和后面执行一些代码,而不必改动被装饰的函数本身。

手动创建装饰器

你如何手动构建一个装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 装饰是一个函数,该函数需要另一个函数作为它的参数
def my_shiny_new_decorator(a_function_to_decorate):

# 在装饰器的函数实现里面它定义了另一个函数: 他就是封装函数(wrapper)
# 这个函数将原来的函数封装到里面
# 因此你可以在原来函数的前面和后面执行一些附加代码
def the_wrapper_around_the_original_function():

# 在这里放置你想在原来函数执行前执行的代码
print "Before the function runs"

# 调用原来的函数(使用圆括号)
a_function_to_decorate()

# 在这里放置你想在原来函数执行后执行的代码
print "After the function runs"

# 这个时候,"a_function_to_decorate"并没有执行
# 我们返回刚才创建的封装函数
# 这个封装函数包含了原来的函数,和将在原来函数前面和后面执行的代码。我们就可以使用它了!
return the_wrapper_around_the_original_function

# 想象你创建了一个你再也不想修改的函数
def a_stand_alone_function():
print "I am a stand alone function, don't you dare modify me"

a_stand_alone_function()
# 输出为: I am a stand alone function, don't you dare modify me

# 现在你可以装饰这个函数来扩展它的行为
# 只需要将这个函数传入装饰器,那它将被动态的包在任何你想执行的代码间,并且返回一个可被使用的新函数:
a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#输出为:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

现在,你可能想在每次调用 a_stand_alone_function的时候,真正被执行的函数是 a_stand_alone_function_decorated。那很容易,只需要使用 my_shiny_new_decorator返回的函数赋值给原来的 a_stand_alone_function这个函数名(其实是个变量):

1
2
3
4
5
6
7
8
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#输出为:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

# 你猜怎么着?这就是装饰器做的事情。

装饰器揭秘

前面的例子,使用Python的装饰器语法糖来重写就是下面的样子:

1
2
3
4
5
6
7
8
9
@my_shiny_new_decorator
def another_stand_alone_function():
print "Leave me alone"

another_stand_alone_function()
# 输出为:
#Before the function runs
#Leave me alone
#After the function runs

是的,这就是全部,就是这么简单。@decorator 只是下面表达式的简写:

1
another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

这里的装饰器只是装饰器设计模式 的一种Python化变体。Python嵌入了多种经典的设计模式来简化开发(比如迭代器(iterators))。
当然,你可以堆积装饰器(使用多层装饰器):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def bread(func):
def wrapper():
print "</''''''\>;"
func()
print "<\______/>;"
return wrapper

def ingredients(func):
def wrapper():
print "#tomatoes#"
func()
print "~salad~"
return wrapper

def sandwich(food="--ham--"):
print food

sandwich()
# 输出为: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs:
#</''''''\>;
# #tomatoes#
# --ham--
# ~salad~
#<\______/>;

使用Python的装饰器语法糖:

1
2
3
4
5
6
7
8
9
10
11
12
@bread
@ingredients
def sandwich(food="--ham--"):
print food

sandwich()
#outputs:
#</''''''\>;
# #tomatoes#
# --ham--
# ~salad~
#<\______/>;

你放置装饰器的顺序很重要:

1
2
3
4
5
6
7
8
9
10
11
12
@ingredients
@bread
def strange_sandwich(food="--ham--"):
print food

strange_sandwich()
#outputs:
##tomatoes#
#</''''''\>;
# --ham--
#<\______/>;
# ~salad~

现在: 回答问题(请参考stack overflorw 上的相关问题)

作为结论,你可以很容易看出如何回答问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 使其变bold的装饰器
def makebold(fn):
# 装饰器将要返回的函数
def wrapper():
# 在原函数前面和后面插入一些代码
return "<b>;" + fn() + "</b>;"
return wrapper

# 使其变italic的装饰器
def makeitalic(fn):
# 装饰器将要返回的函数
def wrapper():
# 在原函数前面和后面插入一些代码
return "<i>;" + fn() + "</i>;"
return wrapper

@makebold
@makeitalic
def say():
return "hello"

print say()
# 输出为: <b>;<i>;hello</i>;</b>;

# 这和下面代码效果相同
def say():
return "hello"
say = makebold(makeitalic(say))

print say()
# 输出为: <b>;<i>;hello</i>;</b>;

你可以高兴的离开这里了,或者再费点脑子来看看装饰器的高级用法。

更深入的讨论装饰器

向被装饰的函数传参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 这不是黑魔法,你只需要让封装函数传递这些参数:

def a_decorator_passing_arguments(function_to_decorate):
def a_wrapper_accepting_arguments(arg1, arg2):
print "I got args! Look:", arg1, arg2
function_to_decorate(arg1, arg2)
return a_wrapper_accepting_arguments

# 因为当你调用被装饰器返回的函数时,实际你是在调用封装函数
# 所以向封装函数传递参数可以让封装函数把参数传递给被装饰的函数

@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
print "My name is", first_name, last_name

print_full_name("Peter", "Venkman")
# 输出为:
# I got args! Look: Peter Venkman
# My name is Peter Venkman

装饰方法

Python中方法和函数几乎是一样的,这个特性很nice。唯一的不同是方法期望它的第一个参数是对当前对象的引用(self)。
那意味着你可以使用相同的方式来给方法添加装饰器!只是需要将self考虑在内:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def method_friendly_decorator(method_to_decorate):
def wrapper(self, lie):
lie = lie - 3 # 很友好吧,再次减少了年龄 :-)
return method_to_decorate(self, lie)
return wrapper

class Lucy(object):

def __init__(self):
self.age = 32

@method_friendly_decorator
def sayYourAge(self, lie):
print "I am %s, what did you think?" % (self.age + lie)

l = Lucy()
l.sayYourAge(-3)
# 输出为: I am 26, what did you think?

如果你在写一个通用的装饰器—可以接收任何参数的函数或者方法—这时候只需要使用 *args, **kwargs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def a_decorator_passing_arbitrary_arguments(function_to_decorate):
# 封装函数可以接收任何的参数
def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
print "Do I have args?:"
print args
print kwargs
# 然后你解包出参数,这里是 *args, **kwargs
# 如果你不熟悉怎么解包,可以查看:
# http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
function_to_decorate(*args, **kwargs)
return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
print "Python is cool, no argument here."

function_with_no_argument()
#输出为:
#Do I have args?:
#()
#{}
#Python is cool, no argument here.

@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
print a, b, c

function_with_arguments(1,2,3)
# 输出为:
#Do I have args?:
#(1, 2, 3)
#{}
#1 2 3

@a_decorator_passing_arbitrary_arguments
def function_with_named_arguments(a, b, c, platypus="Why not ?"):
print "Do %s, %s and %s like platypus? %s" %\
(a, b, c, platypus)

function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")
# 输出为:
#Do I have args ? :
#('Bill', 'Linus', 'Steve')
#{'platypus': 'Indeed!'}
#Do Bill, Linus and Steve like platypus? Indeed!

class Mary(object):

def __init__(self):
self.age = 31

@a_decorator_passing_arbitrary_arguments
def sayYourAge(self, lie=-3): # 这时候你可以添加一个默认参数值
print "I am %s, what did you think ?" % (self.age + lie)

m = Mary()
m.sayYourAge()
# 输出为:
# Do I have args?:
#(<__main__.Mary object at 0xb7d303ac>;,)
#{}
#I am 28, what did you think?

给装饰器传递参数

好了,现在你觉得给装饰器本身传递参数该怎么做呢?
这个可能有点绕,因为装饰器必须接收一个函数作为参数。
因此,你不能把被装饰函数的参数直接传递给装饰器。
在我们说出解决办法前,写点代码来找找灵感:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 装饰器只是普通的函数
def my_decorator(func):
print "I am an ordinary function"
def wrapper():
print "I am function returned by the decorator"
func()
return wrapper

# 因此,你可以不使用任何的 "@" 就可以调用它

def lazy_function():
print "zzzzzzzz"

decorated_function = my_decorator(lazy_function)
# 输出为: I am an ordinary function

# 它输出 "I am an ordinary function",因为那就是你在代码里面做的事情:
# 调用一个函数,没有任何的魔法。

@my_decorator
def lazy_function():
print "zzzzzzzz"

# 输出为: I am an ordinary function

上面两种方式几乎一样。my_decorator被调用。因此当你在代码里面添加 @my_decorato时,你就在告诉Python去调用被my_decorator变量标示的函数。
这很重要! 你给出的这个变量名可以直接指向装饰器也可以不直接指向
我们来干点邪恶的事情。 ☺

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def decorator_maker():

print "I make decorators! I am executed only once: "+\
"when you make me create a decorator."

def my_decorator(func):

print "I am a decorator! I am executed only when you decorate a function."

def wrapped():
print ("I am the wrapper around the decorated function. "
"I am called when you call the decorated function. "
"As the wrapper, I return the RESULT of the decorated function.")
return func()

print "As the decorator, I return the wrapped function."

return wrapped

print "As a decorator maker, I return a decorator"
return my_decorator

# 我们创建了一个装饰器。它就只是一个新的函数。
new_decorator = decorator_maker()
# 输出为:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator

# 然后我们装饰一个函数
def decorated_function():
print "I am the decorated function."

decorated_function = new_decorator(decorated_function)
# 输出为:
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function

# 我们调用这个函数:
decorated_function()
# 输出为:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

这里没有任何惊奇的地方。
让我们再次来做相同的事情,但是省略掉所有讨厌的中间变量:
def decorated_function():
print "I am the decorated function."
decorated_function = decorator_maker()(decorated_function)
# 输出为:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

# 最后:
decorated_function()
# 输出为:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

让我们使它更简洁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@decorator_maker()
def decorated_function():
print "I am the decorated function."
# 输出为:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

# 最终:
decorated_function()
# 输出为:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

Hey,你注意到了吗?我们除了 @格式的语法糖外还使用了函数调用! :-)
因此,回到带参数装饰器的讨论。如果我们可以使用函数来创建装饰器,我们就可以把参数传递给那个函数,对吧?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):

print "I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2

def my_decorator(func):
# 这里之所有可以传递参数,得益于closures的特性。
# 如果你不熟悉closures,你可以假设这是没问题的,
# 或者读一下: http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python

print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2

# 不要把装饰器的参数和函数的参数搞混
def wrapped(function_arg1, function_arg2) :
print ("I am the wrapper around the decorated function.\n"
"I can access all the variables\n"
"\t- from the decorator: {0} {1}\n"
"\t- from the function call: {2} {3}\n"
"Then I can pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,
function_arg1, function_arg2))
return func(function_arg1, function_arg2)

return wrapped

return my_decorator

@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
print ("I am the decorated function and only knows about my arguments: {0}"
" {1}".format(function_arg1, function_arg2))

decorated_function_with_arguments("Rajesh", "Howard")
# 输出为:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#I am the wrapper around the decorated function.
#I can access all the variables
# - from the decorator: Leonard Sheldon
# - from the function call: Rajesh Howard
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard

这就是它了:带参数的装饰器。参数可以使用变量来设定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
c1 = "Penny"
c2 = "Leslie"

@decorator_maker_with_arguments("Leonard", c1)
def decorated_function_with_arguments(function_arg1, function_arg2):
print ("I am the decorated function and only knows about my arguments:"
" {0} {1}".format(function_arg1, function_arg2))

decorated_function_with_arguments(c2, "Howard")
# 输出为:
#I make decorators! And I accept arguments: Leonard Penny
#I am the decorator. Somehow you passed me arguments: Leonard Penny
#I am the wrapper around the decorated function.
#I can access all the variables
# - from the decorator: Leonard Penny
# - from the function call: Leslie Howard
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Leslie Howard

正如你看到的那样,你可以使用这个技巧像给函数传递参数一样给装饰器传递参数。如果你想,你甚至可以使用 *args, **kwargs。但是记住装饰器只会被调用一次。仅仅当Python载入(imports)这个脚本的时候调用。之后你不可以动态的设定参数。当你 “import x”的时候, 这个函数已经被装饰了,因此你不能再改变任何东西了。

让我们来练习一下:装饰一个装饰器

好了,作为福利,我将给你一个代码片段,它可以让装饰器接收任何参数。
为了能接收参数,我们使用另一个函数来创建我们的装饰器。
我们封装了装饰器。
我们最近知道的能封装函数的东西是什么?
对,就是装饰器
让我们来写一个装饰装饰器的装饰器来玩玩(够绕吧):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def decorator_with_args(decorator_to_enhance): 
"""
这个函数被当做装饰器来使用。
它必须装饰另一个函数,这个函数也被当做装饰器使用
感觉理解不过来,休息一下
它允许任何的装饰器接收任何参数。
在你的脑子里面记住每次应该怎样构建这样的装饰器
"""

# 我们使用相同的技巧来传递参数
def decorator_maker(*args, **kwargs):

# 我们动态的创建一个接收一个函数作为参数的装饰器
# 保持住从decorator_maker传递过来的参数。
def decorator_wrapper(func):

# 我们最后返回原始装饰器的结果,
# 这个结果就是最原始被装饰的函数 (就是返回一个函数)。
# 这里只有一个小缺陷:被封装的装饰器必须具有特定的签名,不然它不会工作
return decorator_to_enhance(func, *args, **kwargs)这个样子

return decorator_wrapper

return decorator_maker

它可以像下面这样使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 你创建一个将要被用作装饰器的函数。并向它添加一个装饰器(这里是指decorator_with_args) :-)
# 不要忘了你创建的这个装饰器签名必须是 "decorator(func, *args, **kwargs)"
@decorator_with_args
def decorated_decorator(func, *args, **kwargs):
def wrapper(function_arg1, function_arg2):
print "Decorated with", args, kwargs
return func(function_arg1, function_arg2)
return wrapper

# 之后你使用上面定义的装饰器(指decorated_decorator)来装饰一个函数
@decorated_decorator(42, 404, 1024)
def decorated_function(function_arg1, function_arg2):
print "Hello", function_arg1, function_arg2

decorated_function("Universe and", "everything")
# 输出为:
#Decorated with (42, 404, 1024) {}
#Hello Universe and everything

# 喔!

我知道,你有了这样一种感觉,它是在听一一个家伙说:”before understanding recursion, you must first understand recursion”产生的。但是,现在,你是否有更好的体会了?

最佳实践: 装饰器

  • 装饰器是Python2.4引入的,因此保证你的代码运行的版本 >;= 2.4。
  • 装饰器会拖慢函数的执行速度,记住这点。
  • 你不能反装饰一个函数。因此一旦一个函数被装饰了,它对所有其它代码来说就都是被装饰了的。
  • 装饰器会封装函数,这会让它们变得更难调试。 (这点从Python >;= 2.5版本变得好了起来; 具体参考下面。)

在Python 2.5中 functools模块被引入。它包含了functools.wraps()函数,这个函数会将被装饰函数的名称、模块、文档字符串拷贝到封装函数。
(有趣的事实是:functools.wraps() 也是一个装饰器! ☺)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 为了调试,在堆栈轨迹中打印了函数的名称(__name__)
def foo():
print "foo"

print foo.__name__
# 输出为 : foo

# 有了装饰器,名称就凌乱了
def bar(func):
def wrapper():
print "bar"
return func()
return wrapper

@bar
def foo():
print "foo"

print foo.__name__
# 输出为: wrapper

# "functools"对这个有帮助

import functools

def bar(func):
# 我们看到"wrapper"封装了"func"
# 现在魔法开始了
@functools.wraps(func)
def wrapper():
print "bar"
return func()
return wrapper

@bar
def foo():
print "foo"

print foo.__name__
# 输出为: foo

装饰器可以被用在什么地方?

现在问题来了:我可以使用装饰器来干什么?
尽管看起来很酷很强大,但是一个实际的应用事例能更好的说明问题。好了,这里有1000种可能性。经典的使用是在库以外的代码中扩展一个函数的行为 (你不能修改的函数),或者只是为了调试 (因为调试只是零时的,所有你不想修改这个函数)。
你可以以一种DRY的方式使用他们来扩展许多函数,像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def benchmark(func):
"""
打印原函数调用时间的装饰器
"""
import time
def wrapper(*args, **kwargs):
t = time.clock()
res = func(*args, **kwargs)
print func.__name__, time.clock()-t
return res
return wrapper


def logging(func):
"""
记录脚本行为日志的装饰器
(这里只是打印它,但是也可以记录到日志里面!)
"""
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
print func.__name__, args, kwargs
return res
return wrapper


def counter(func):
"""
记录并打印一个函数执行次数的装饰器
"""
def wrapper(*args, **kwargs):
wrapper.count = wrapper.count + 1
res = func(*args, **kwargs)
print "{0} has been used: {1}x".format(func.__name__, wrapper.count)
return res
wrapper.count = 0
return wrapper

@counter
@benchmark
@logging
def reverse_string(string):
return str(reversed(string))

print reverse_string("Able was I ere I saw Elba")
print reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!")

#输出为:
#reverse_string ('Able was I ere I saw Elba',) {}
#wrapper 0.0
#wrapper has been used: 1x
#ablE was I ere I saw elbA
#reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {}
#wrapper 0.0
#wrapper has been used: 2x
#!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)lemac a ro( niaga gab ananab a ,gat a ,nat a ,gab ananab a ,gag a ,inoracam ,elacrep ,epins ,spam ,arutaroloc a ,shajar ,soreh ,atsap ,eonac a ,nalp a ,nam A

当然,装饰器最好的一点是你不需要重写,就可以几乎在任何东西上面使用它们。这就是我所说的,DRY:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@counter
@benchmark
@logging
def get_random_futurama_quote():
from urllib import urlopen
result = urlopen("http://subfusion.net/cgi-bin/quote.pl?quote=futurama").read()
try:
value = result.split("<br>;<b>;<hr>;<br>;")[1].split("<br>;<br>;<hr>;")[0]
return value.strip()
except:
return "No, I'm ... doesn't!"

print get_random_futurama_quote()
print get_random_futurama_quote()

#outputs:
#get_random_futurama_quote () {}
#wrapper 0.02
#wrapper has been used: 1x
#The laws of science be a harsh mistress.
#get_random_futurama_quote () {}
#wrapper 0.01
#wrapper has been used: 2x
#Curse you, merciful Poseidon!

Python语言本身提供了好多种装饰器:property, staticmethod,等等。

  • Django使用装饰器来管理缓存和视图权限。
  • Twisted to fake inlining asynchronous functions calls.(对Twisted不熟,不知道具体讲的是神马!)。

这个真是一个巨大的游乐场